package com.personal.system.service.impl.act;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.personal.model.system.SysDept;
import com.personal.model.system.SysRole;
import com.personal.model.system.SysUser;
import com.personal.system.config.SecurityUtil;
import com.personal.system.entity.approval.*;
import com.personal.system.exception.GlobalException;
import com.personal.system.mapper.act.AuditRuleMapper;
import com.personal.system.service.act.AuditRuleDetailService;
import com.personal.system.service.act.AuditRuleService;
import com.personal.system.service.sys.SysDeptService;
import com.personal.system.service.sys.SysRoleService;
import com.personal.system.service.sys.SysUserService;
import com.personal.system.utils.ActivitiUtils;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.Process;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.DeploymentQuery;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 审批规则Service业务层处理
 *
 * @author hhdd
 * @date 2023-02-17 09:57:45
 */
@Slf4j
@Service
public class AuditRuleServiceImpl extends ServiceImpl<AuditRuleMapper, AuditRule> implements AuditRuleService {

    @Resource
    private AuditRuleMapper auditRuleMapper;

    @Resource
    private AuditRuleDetailService detailService;

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private SecurityUtil securityUtil;

    @Resource
    private SysDeptService deptService;
    @Resource
    private SysRoleService roleService;
    @Resource
    private SysUserService userService;

    /**
     * 查询审批规则
     *
     * @param ruleId 审批规则ID
     * @return 审批规则
     */
    @Override
    public AuditRule selectAuditRuleById(String ruleId) {

        AuditRule auditRule = auditRuleMapper.selectById(ruleId);

        AuditRuleVo auditRuleVo = BeanUtil.copyProperties(auditRule, AuditRuleVo.class);

        String auditSubmitUser = auditRuleVo.getAuditSubmitUser();
        if (StrUtil.isNotBlank(auditSubmitUser)){
            String[] submitUser = auditRuleVo.getAuditSubmitUser().split(",");
            auditRuleVo.setSubRen(Lists.newArrayList(submitUser));
        }

        String auditTrigger = auditRuleVo.getAuditTrigger();
        if (StrUtil.isNotBlank(auditTrigger)){
            auditRuleVo.setSubDept(Lists.newArrayList(auditTrigger.split(",")));
        }

        LambdaQueryWrapper<AuditRuleDetail> qw = Wrappers.lambdaQuery(AuditRuleDetail.class)
                .eq(AuditRuleDetail::getRuleId, auditRule.getRuleId())
                .eq(AuditRuleDetail::getProcessType, "02")
                .orderByAsc(AuditRuleDetail::getSort);
        List<AuditRuleDetail> detailList = detailService.list(qw);
        List<String> appRen = detailList.stream().map(e -> e.getRole()).collect(Collectors.toList());
        auditRuleVo.setApprovalRen(appRen);

        return auditRuleVo;
    }

    @Override
    public IPage<AuditRule> selectAuditRuleList(Page<AuditRule> pageParam, AuditRule auditRule) {

        QueryWrapper<AuditRule> qw = new QueryWrapper<>();
        qw.select("*,( SELECT GROUP_CONCAT( DISTINCT rule_detail_id ) FROM audit_rule_detail WHERE rule_id = audit_rule.rule_id and process_type ='02') approvalLevel");
        qw.lambda().like(StrUtil.isNotBlank(auditRule.getCompName()),AuditRule::getCompName, auditRule.getCompName());
        qw.lambda().like(StrUtil.isNotBlank(auditRule.getRuleName()),AuditRule::getRuleName, auditRule.getRuleName());
        qw.lambda().like(StrUtil.isNotBlank(auditRule.getRuleNo()),AuditRule::getRuleNo, auditRule.getRuleNo());

        Page<AuditRule> auditRulePage = auditRuleMapper.selectPage(pageParam, qw);
        for (AuditRule record : auditRulePage.getRecords()) {
            String approvalLevel = record.getApprovalLevel();
            if (StrUtil.isNotBlank(approvalLevel))
                record.setApprovalLevel(approvalLevel.split(",").length+"");
        }

        return auditRulePage;
    }

    /**
     * 新增审批规则
     */
    @Transactional(rollbackFor = Exception.class)
    public int insertAuditRule(AuditRuleVo auditRuleVo) {
        String ruleId = UUID.randomUUID().toString(true);
        checkRepeatRule(auditRuleVo);
        auditRuleVo.setRuleId(ruleId);
        auditRuleVo.setRuleNo("G"+new Snowflake().nextIdStr().substring(0,9));
        auditRuleVo.setIsDelete("00");
        auditRuleVo.setCreateUser(securityUtil.getUserName());

        AuditRuleDetail detail = new AuditRuleDetail();
        detail.setRuleId(ruleId);

        List<String> subDept = auditRuleVo.getSubDept();
        if (CollUtil.isNotEmpty(subDept)){
            String deptJo = subDept.stream().collect(Collectors.joining(","));
            auditRuleVo.setAuditTrigger(deptJo);
            detail.setProcessType("01");
            detail.setRoleType("02");
            addRuleDetail(detail, subDept);
        }
        List<String> subRen = auditRuleVo.getSubRen();
        if (CollUtil.isNotEmpty(subRen)){
            String renJo = subRen.stream().collect(Collectors.joining(","));
            auditRuleVo.setAuditSubmitUser(renJo);
            detail.setProcessType("01");
            detail.setRoleType("01");
            addRuleDetail(detail, subRen);
        }

        List<String> approvalRen = auditRuleVo.getApprovalRen();
        if (CollUtil.isNotEmpty(approvalRen)){
            detail.setProcessType("02");
            detail.setRoleType("01");
            addRuleDetail(detail, approvalRen);
        }

        AuditRule auditRule = BeanUtil.copyProperties(auditRuleVo, AuditRule.class);
        if (auditRule.getRuleClass().contains("-")) auditRule.setRuleClass(auditRule.getRuleClass().split("-")[1]);
        int cou = auditRuleMapper.insert(auditRule);

        if (CollUtil.isNotEmpty(approvalRen))
            createProcessDeployProcess(auditRule);

        return cou;
    }

    private void addRuleDetail(AuditRuleDetail detail, List<String> subRen) {
        for (int i = 0; i < subRen.size(); i++) {
            String detailId = UUID.fastUUID().toString(true);
            detail.setRuleDetailId(detailId);
            detail.setRole(subRen.get(i));
            detail.setSort(i+1);
            detailService.save(detail);
        }
    }

    private void checkRepeatRule(AuditRuleVo auditRuleVo) {
        LambdaQueryWrapper<AuditRule> qw = new LambdaQueryWrapper<>();
        qw.eq(AuditRule::getIsDelete, "00");
        qw.eq(AuditRule::getCompName, auditRuleVo.getCompName());
        qw.eq(AuditRule::getRuleName, auditRuleVo.getRuleName());
        List<AuditRule> list1 = this.list(qw);
        if (CollUtil.isNotEmpty(list1)) throw new GlobalException(404, "销售主体：" + auditRuleVo.getCompName() + "，已存在相同的流程名称!");

        qw.eq(AuditRule::getApprovalModule, auditRuleVo.getApprovalModule());
        qw.eq(AuditRule::getAuditType, auditRuleVo.getAuditType());
        List<String> subDept = auditRuleVo.getSubDept();
        if (CollUtil.isNotEmpty(subDept)){
            String joinDept = String.join(",", subDept);
            qw.apply("audit_trigger REGEXP REPLACE ({0} , ',', '|') = 1",joinDept);
        }
        List<String> subRen = auditRuleVo.getSubRen();
        if (CollUtil.isNotEmpty(subRen)){
            String joinRen = String.join(",", subRen);
            qw.apply("audit_submit_user REGEXP REPLACE ({0} , ',', '|') = 1",joinRen);
        }

        List<AuditRule> list2 = this.list(qw);
        if (CollUtil.isNotEmpty(list2)) throw new GlobalException(404, "销售主体：" + auditRuleVo.getCompName() + "，存在相同模块、模式、类型、二级子集名称、提交人的数据已存在!");
    }

    private void createProcessDeployProcess(AuditRule auditRule) {

        LambdaQueryWrapper<AuditRuleDetail> qw = Wrappers.lambdaQuery(AuditRuleDetail.class)
                .eq(AuditRuleDetail::getRuleId, auditRule.getRuleId())
                .eq(AuditRuleDetail::getProcessType, "02");
        List<AuditRuleDetail> detailList = detailService.list(qw);

        List<UserTaskVo> userTaskList = new ArrayList<>();
        UserTaskVo userTaskVo = new UserTaskVo();
        userTaskVo.setUserId( "${applyUserId}");
        userTaskVo.setTaskName( "提交审批单");
        userTaskList.add(userTaskVo);

        for (AuditRuleDetail ruleDetail : detailList) {
            UserTaskVo taskVo = new UserTaskVo();
            taskVo.setUserId( ruleDetail.getRole());
            taskVo.setTaskName( ruleDetail.getRole());
            taskVo.setCategory( ruleDetail.getRoleType());
            taskVo.setAssignee( ruleDetail.getRole());
            userTaskList.add(taskVo);
        }
        String processName = auditRule.getRuleName();
        String processKey = auditRule.getRuleNo();

        createProcessDeployProcess(new ApproveVo().getTenantId(),processKey,processName, userTaskList);
    }

    // 动态创建流程
    public void createProcessDeployProcess(String tenantId, String processKey, String processName, List<UserTaskVo> userTaskList) {
        // 根据部署工作流KEY和公司ID查询已部署最新工作流的信息
        if (StrUtil.isBlank(processKey)) processKey = StrUtil.EMPTY;
        DeploymentQuery deploymentQuery = repositoryService.createDeploymentQuery().deploymentKey(processKey)
                .deploymentTenantId(processKey);
        // 如果有了先删除
        Deployment deployment = deploymentQuery.latest().singleResult();
        if (null != deployment){
            repositoryService.deleteDeployment(processKey, true);
            log.info("删除流程部署，ID：{}", processKey);
        }

        BpmnModel model = new BpmnModel();
        Process process = new Process();
        model.addProcess(process);
        process.setId(processKey);
        process.setName(processName);
        // 构造开始节点
        process.addFlowElement(ActivitiUtils.createStartEvent());

        for (int i = 0; i < userTaskList.size(); i++) {
            if (i == 0){
                process.addFlowElement(ActivitiUtils.createUserTask("submitUserTask", userTaskList.get(i)));
            } else {
                process.addFlowElement(ActivitiUtils.createUserTask("task" + i, userTaskList.get(i)));
            }
        }

        process.addFlowElement(ActivitiUtils.createSequenceFlow("start", "submitUserTask", null, null));

        for (int i = 0; i < userTaskList.size(); i++) {
            if (i == 0) {
                process.addFlowElement(
                        ActivitiUtils.createSequenceFlow("submitUserTask", "task" + (i + 1), "提交", null));
            } else {
                if (i == userTaskList.size() - 1) {
                    process.addFlowElement(
                            ActivitiUtils.createSequenceFlow("task" + i, "end", "通过", "${result == '1'}"));
                } else {
                    process.addFlowElement(
                            ActivitiUtils.createSequenceFlow("task" + i, "task" + (i + 1), "通过", "${result == '1'}"));
                }
                process.addFlowElement(
                        ActivitiUtils.createSequenceFlow("task" + i, "submitUserTask", "驳回", "${result == '0'}"));
            }
        }

        process.addFlowElement(ActivitiUtils.createEndEvent());

        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
        Deployment deploy = deploymentBuilder.key(processKey).name(processName).tenantId(tenantId)
                                .addBpmnModel("布署名:" + processName + ".bpmn", model).deploy();

        log.info("=============流程布署ID={}" , deploy.getId());
    }

    /**
     * 修改审批规则
     */
    @Transactional(rollbackFor = Exception.class)
    public int updateAuditRule(AuditRuleVo auditRuleVo) {
        String ruleId = auditRuleVo.getRuleId();
        if (StrUtil.isBlank(ruleId)) throw new GlobalException(404,"规则ID不能为空!");

        AuditRule rule = auditRuleMapper.selectById(ruleId);
        if (null == rule) throw new GlobalException(404,"审核规则不存在!");

        auditRuleMapper.deleteById(ruleId);
        detailService.remove(Wrappers.lambdaQuery(AuditRuleDetail.class).eq(AuditRuleDetail::getRuleId,rule.getRuleId()));

        List<Deployment> list = repositoryService.createDeploymentQuery().deploymentKey(auditRuleVo.getRuleNo()).list();
        if (CollUtil.isNotEmpty(list))
            repositoryService.deleteDeployment(list.get(0).getId(), true);

        return this.insertAuditRule(auditRuleVo);
    }

    @Override
    public int updateState(AuditRule auditRule) {
        auditRule.setUpdateTime(new Date());
        auditRule.setUpdateName(securityUtil.getUserName());
        if (auditRule.getIsDelete().equals("00")){
            auditRule.setIsDelete("01");
        } else { auditRule.setIsDelete("00"); }
        return auditRuleMapper.updateById(auditRule);
    }

    /**
     * 批量删除审批规则
     *
     * @param ruleIds 需要删除的审批规则ID
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    public int deleteAuditRuleByIds(String[] ruleIds) {

        for (String ruleId : ruleIds) {
            AuditRule auditRule = auditRuleMapper.selectById(ruleId);
            List<Deployment> list = repositoryService.createDeploymentQuery().deploymentKey(auditRule.getRuleNo()).list();
            if (CollUtil.isNotEmpty(list))
                repositoryService.deleteDeployment(list.get(0).getId(), true);
        }

        int cou = 0;
        cou += auditRuleMapper.deleteBatchIds(Arrays.asList(ruleIds.clone()));

        LambdaQueryWrapper<AuditRuleDetail> qw = new LambdaQueryWrapper<>();
        qw.in(AuditRuleDetail::getRuleId, Arrays.asList(ruleIds));
        detailService.remove(qw);

        return cou;
    }

    /**
     * 删除审批规则信息
     */
    @Override
    public int deleteAuditRuleById(String ruleId) {
        return auditRuleMapper.deleteById(ruleId);
    }

    @Override
    public Object getDeptRoleUser() {

        List<SysDept> deptList = deptService.findNodes();

        List<SysRole> roleList = roleService.list();

        List<SysUser> userList = userService.list();

        Map<String, Object> map = MapUtil.builder(new HashMap<String, Object>())
                .put("deptList", deptList).put("roleList", roleList)
                .put("userList", userList).build();

        return map;
    }

    @Override
    public List<Map<String, String>> appDetails(String ruleId) {

        LambdaQueryWrapper<AuditRuleDetail> qw = Wrappers.<AuditRuleDetail>lambdaQuery().eq(AuditRuleDetail::getRuleId, ruleId).orderByAsc(AuditRuleDetail::getProcessType, AuditRuleDetail::getSort);

        List<AuditRuleDetail> list = detailService.list(qw);
        List<Map<String, String>> mapList = new ArrayList<>();
        for (AuditRuleDetail detail : list) {
            Map<String, String> map = new HashMap<>();
            if (detail.getRoleType().equals("01")){
                map.put("person",userService.getById(detail.getRole()).getName());
            } else {
                map.put("person",deptService.getById(detail.getRole()).getName());
            }

            if ("01".equals(detail.getProcessType())){
                map.put("role","提审人");
            } else {
                String chin = Convert.numberToChinese(detail.getSort(),false);
                map.put("role",chin + "级审批人");
            }
            mapList.add(map);
        }

        return mapList;
    }

    @Override
    public AuditRule getAuditRule(AuditRule auditRule) {

        return auditRuleMapper.getAuditRule(auditRule);
    }

}
